﻿using AiQiuQuan.Sport.Core.Hangfire;
using Hangfire;
using Hangfire.Dashboard;
using Hangfire.Redis;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace AiQiuQuan.Sport.Core
{
    /// <summary>
    /// hangfire扩展
    /// </summary>
    public static class HangfireExtension
    {
        /// <summary>
        /// hangfire by redis
        /// </summary>
        public static void AddRedisHangfire(this IServiceCollection services, string redisConnection)
        {
            var db = 0;
            if (!string.IsNullOrWhiteSpace(redisConnection))
            {
                var dbstr = redisConnection.Split(',').FirstOrDefault(m => m.Trim().StartsWith("DefaultDatabase") && m.Contains("="));
                if (!string.IsNullOrWhiteSpace(dbstr))
                {
                    int.TryParse(dbstr.Split('=')[1], out db);
                }
            }

            GlobalStateHandlers.Handlers.Add(new SucceededStateExpireHandler(60));
            services.AddHangfire(globalConfig =>
            {
                globalConfig.UseRedisStorage(redisConnection, new RedisStorageOptions
                {
                    UseTransactions = true,
                    DeletedListSize = 10,
                    SucceededListSize = 10,
                    Db = db
                });
            });
            services.AddHangfireServer(jobServerOption =>
            {

            });
        }

        /// <summary>
        /// hangfire middleware
        /// </summary>
        /// <param name="app">IApplicationBuilder</param>
        /// <param name="path">访问绝对路径</param>
        public static IApplicationBuilder UseRedisHangfire(this IApplicationBuilder app, string path)
        {
            path = string.IsNullOrEmpty(path) ? "/hangfire" : path;
            app.UseHangfireDashboard(path, new DashboardOptions
            {
                IsReadOnlyFunc = (dashContext) =>
                {
                    var context = dashContext.GetHttpContext();
                    if (context != null)
                    {
                        var referer = context.Request.Headers["Referer"];
                        if (context.Request.Query.ContainsKey("admin") || referer.ToString().Contains("recurring?admin"))
                        {
                            return false;
                        }
                    }

                    return true;
                },
                DisplayStorageConnectionString = false,
                DashboardTitle = "爱球圈定时任务",
                Authorization = new List<IDashboardAuthorizationFilter>
                {
                    new NoAuthHangfireFilter()
                }
            });

            var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
            RegisterReccuringJob(scopeFactory);
            return app;
        }

        /// <summary>
        /// 注册 hangfire循环作业
        /// </summary>
        private static void RegisterReccuringJob(IServiceScopeFactory scopeFactory)
        {
            using (var scope = scopeFactory.CreateScope())
            {
                var hangfireServiceList = scope.ServiceProvider.GetServices<IHangfireService>();
                foreach (var item in hangfireServiceList)
                {
                    var type = item.GetType();
                    var cron = GetCron(type);
                    if (!string.IsNullOrWhiteSpace(cron))
                    {
                        RecurringJob.AddOrUpdate(type.FullName.ToLower(), () => item.ExecuteAsync(), cron, TimeZoneInfo.Local);
                    }
                }
            }
        }

        /// <summary>
        /// 获取 ReccuringJobCronAttribute 表达式
        /// </summary>
        private static string GetCron(Type type)
        {
            if (type != null)
            {
                var customAttributeList = type.GetCustomAttributes(false);
                foreach (var custom in customAttributeList)
                {
                    if (custom is ReccuringJobCronAttribute cron)
                    {
                        return cron.Cron;
                    }
                }
            }

            return null;
        }
    }
}
